home *** CD-ROM | disk | FTP | other *** search
/ Aminet 2 / Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso / Aminet / comm / misc / xprz31.lha / XprZmodem / Receive.c < prev    next >
C/C++ Source or Header  |  1993-08-17  |  19KB  |  678 lines

  1. /**********************************************************************
  2.  * Receive.c:  File reception routines for xprzmodem.library;
  3.  * Version 2.10, 12 February 1991, by Rick Huebner.
  4.  * Based closely on Chuck Forsberg's rz.c example ZModem code,
  5.  * but too pervasively modified to even think of detailing the changes.
  6.  * Released to the Public Domain; do as you like with this code.
  7.  *
  8.  * Version 2.50, 15 November 1991, CRC-32 additions by William M. Perkins.
  9.  * Version 2.63, 30 July 1993 build in locale, by Rainer Hess
  10.  * Version 3.1,  17 August 1993, added Auto-Blocksize by Rainer Hess
  11.  **********************************************************************/
  12.  
  13. #include "xprzmodem_all.h"
  14.  
  15. #define CATCOMP_NUMBERS
  16. #include "xprzmodem_catalog.h"
  17.  
  18. #ifdef DEBUGLOG
  19. extern void *DebugLog;
  20. #endif
  21.  
  22. /**********************************************************
  23.  *      long XProtocolReceive(struct XPR_IO *xio)
  24.  *
  25.  * Main file reception routine; called by comm program
  26.  **********************************************************/
  27. long __saveds __asm
  28. XProtocolReceive (register __a0 struct XPR_IO *xio)
  29. {
  30.   struct SetupVars *sv;
  31.   struct Vars *v;
  32.   UBYTE err = FALSE;
  33.  
  34.   /* Perform common setup and initializations */
  35.   if (!(v = setup (xio)))
  36.     return XPRS_FAILURE;
  37.  
  38.   v->Tryzhdrtype = ZRINIT;
  39.   v->Rxtimeout = 100;
  40.  
  41.   sv = (void *) v->io.xpr_data;
  42.   if (sv->bufpos)
  43.     {
  44.       v->Modemchar = v->Modembuf;
  45.       if (sv->buflen > sizeof (v->Modembuf))
  46.     sv->buflen = sizeof (v->Modembuf);
  47.       memcpy (v->Modembuf, sv->bufpos, sv->buflen);
  48.       v->Modemcount = sv->buflen;
  49.     }
  50.  
  51.   /* Transfer the files */
  52.   if (rcvbatch (v) == ERROR)
  53.     {
  54.       upderr (v, GetLocalString( &li, MSG_DOWNLOAD_USER_ERROR ));
  55.       err = TRUE;
  56.     }
  57.   else
  58.     updmsg (v, GetLocalString( &li, MSG_DONE ));
  59.  
  60.   /* Clean up and return */
  61.   if (v->io.xpr_setserial && v->Oldstatus != -1)
  62.     (*v->io.xpr_setserial) (v->Oldstatus);
  63.   FreeMem (v->Filebuf, v->Filebufmax);
  64.   FreeMem (v, (long) sizeof (struct Vars));
  65.  
  66. #ifdef DEBUGLOG
  67.   if (DebugLog)
  68.     {
  69.       (*v->io.xpr_fclose) ((long) DebugLog);
  70.       DebugLog = NULL;
  71.     }
  72. #endif
  73.  
  74.   return (err) ? XPRS_FAILURE : XPRS_SUCCESS;
  75. }                /* End of long XProtocolReceive() */
  76.  
  77. /**********************************************************
  78.  *      short rcvbatch(struct Vars *v)
  79.  *
  80.  * Start the batch transfer
  81.  **********************************************************/
  82. short
  83. rcvbatch (struct Vars *v)
  84. {
  85. #ifdef DEBUGLOG
  86.   D (DEBUGINFO);
  87. #endif
  88.  
  89.   switch (tryz (v))
  90.     {
  91.     case ZCOMPL:
  92.       return OK;
  93.     case ZFILE:
  94.       if (rzfiles (v) == OK)
  95.     return OK;
  96.     }
  97.  
  98. #ifdef DEBUGLOG
  99.   D (DEBUGINFO);
  100. #endif
  101.  
  102.   canit (v);
  103.  
  104.   return ERROR;
  105. }                /* End of short rcvbatch() */
  106.  
  107. /**********************************************************
  108.  *      short tryz(struct Vars *v)
  109.  *
  110.  * Negotiate with sender to start a file transfer
  111.  **********************************************************/
  112. short
  113. tryz (struct Vars *v)
  114. {
  115.   short n, errors = 0;
  116.  
  117. #ifdef DEBUGLOG
  118.   D (DEBUGINFO);
  119. #endif
  120.  
  121.   for (n = v->ErrorLimit; --n >= 0;)
  122.     {
  123.       /* Set max frame length and capability flags */
  124.       stohdr (v, (long) v->Tframlen);
  125.       v->Txhdr[ZF0] = CANFC32 | CANFDX | CANOVIO;
  126.       zshhdr (v, v->Tryzhdrtype);
  127.       sendbuf (v);
  128.     again:
  129.       /* Check for abort from comm program */
  130.       if (v->io.xpr_chkabort && (*v->io.xpr_chkabort) ())
  131.     return ERROR;
  132.       switch (zgethdr (v))
  133.     {
  134.     case ZFILE:        /* File name and info packet */
  135.       v->Zconv = v->Rxhdr[ZF0];    /* Suggested txt mode; ZCNL = text, */
  136.       /* ZCBIN = binary, 0 = don't know. */
  137.       v->Zmanag = v->Rxhdr[ZF1];    /* Suggested file management mode. */
  138.       v->Ztrans = v->Rxhdr[ZF2];    /* Suggested file transport mode. */
  139.       v->Tryzhdrtype = ZRINIT;
  140.       if (zrdata (v, v->Pktbuf, KSIZE) == GOTCRCW)
  141.         return ZFILE;
  142.       zshhdr (v, ZNAK);    /* Packet mangled, ask for retry */
  143.       sendbuf (v);
  144.       goto again;
  145.     case ZSINIT:        /* Special attention-grabbing string to use to */
  146.       /* interrupt sender */
  147.       if (zrdata (v, v->Attn, ZATTNLEN) == GOTCRCW)
  148.         zshhdr (v, ZACK);
  149.       else
  150.         zshhdr (v, ZNAK);
  151.       sendbuf (v);
  152.       goto again;
  153.     case ZFREECNT:        /* Sender wants to know how much room we've got */
  154.       stohdr (v, getfree ());
  155.       zshhdr (v, ZACK);
  156.       sendbuf (v);
  157.       goto again;
  158.     case ZCOMMAND:        /* Sender wants us to do remote commands, */
  159.       /* but we don't do requests. */
  160.       if (zrdata (v, v->Pktbuf, KSIZE) == GOTCRCW)
  161.         {
  162.           xprsprintf (v->Msgbuf, "%s: %s", GetLocalString( &li, MSG_IGNORING_COMMAND), v->Pktbuf);
  163.           upderr (v, v->Msgbuf);    /* Ignore and report all uploaded commands */
  164.           stohdr (v, 0L);    /* whilst telling sender they worked; */
  165.           do
  166.         {
  167.           zshhdr (v, ZCOMPL);    /* paranoia can be good for you... */
  168.           sendbuf (v);
  169.         }
  170.           while (++errors < v->ErrorLimit && zgethdr (v) != ZFIN);
  171.           ackbibi (v);
  172.           return ZCOMPL;
  173.         }
  174.       else
  175.         zshhdr (v, ZNAK);
  176.       sendbuf (v);
  177.       goto again;
  178.     case ZCOMPL:
  179.       goto again;
  180.     case ZFIN:        /* Sender has ended batch */
  181.       ackbibi (v);
  182.       return ZCOMPL;
  183.     case ZCAN:
  184.     case RCDO:
  185.       upderr (v, v->Msgbuf);
  186.       return ERROR;
  187.     }
  188.     }
  189.   return ERROR;
  190. }                /* End of short tryz() */
  191.  
  192. /**********************************************************
  193.  *      short rzfiles(struct Vars *v)
  194.  *
  195.  * Receive a batch of files
  196.  **********************************************************/
  197. short
  198. rzfiles (struct Vars *v)
  199. {
  200.   struct SetupVars *sv;
  201.   short c;
  202.  
  203. #ifdef DEBUGLOG
  204.   D (DEBUGINFO);
  205. #endif
  206.  
  207.   /* Keep receiving files until end of batch or error */
  208.   while (TRUE)
  209.     {
  210.       switch (c = rzfile (v))
  211.     {
  212.     case ZEOF:
  213.     case ZSKIP:
  214.       switch (tryz (v))
  215.         {
  216.         case ZCOMPL:
  217.           return OK;
  218.         default:
  219.           return ERROR;
  220.         case ZFILE:
  221.           break;
  222.         }
  223.       break;
  224.     default:
  225.       bfclose (v);
  226.       sv = (void *) v->io.xpr_data;
  227.       if (*sv->option_k == 'N' && v->io.xpr_extension >= 2
  228.           && v->io.xpr_unlink)
  229.         {
  230.           updmsg (v, GetLocalString( &li, MSG_DELETING_RECEIVED_FILE ));
  231.           (*v->io.xpr_unlink) (v->Filename);
  232.         }
  233.       else
  234.         updmsg (v, GetLocalString( &li, MSG_KEEPING_RECEIVED_FILE));
  235.       return c;
  236.     }
  237.     }
  238. }                /* End of short rzfiles() */
  239.  
  240. /**********************************************************
  241.  *      short rzfile(struct Vars *v)
  242.  *
  243.  * Receive one file; file name packet already read into
  244.  * Pktbuf by tryz()
  245.  **********************************************************/
  246. short
  247. rzfile (struct Vars *v)
  248. {
  249.   short c, n;
  250.  
  251. #ifdef DEBUGLOG
  252.   D (DEBUGINFO);
  253. #endif
  254.  
  255.   /*
  256.      * Process file name packet; either open file and prepare to receive,
  257.      * or tell us to skip this one.
  258.    */
  259.   if (procheader (v) == ERROR)
  260.     return v->Tryzhdrtype = ZSKIP;
  261.  
  262.   n = v->ErrorLimit;
  263.   v->Rxbytes = v->Strtpos;
  264.   v->Eofseen = FALSE;
  265.  
  266.   /* Receive ZDATA frames until finished */
  267.   while (TRUE)
  268.     {
  269.       stohdr (v, v->Rxbytes);    /* Tell sender where to start frame */
  270.       zshhdr (v, ZRPOS);
  271.       sendbuf (v);
  272.     nxthdr:
  273.       /* Check for abort from comm program */
  274.       if (v->io.xpr_chkabort && (*v->io.xpr_chkabort) ())
  275.     return ERROR;
  276.       switch (c = zgethdr (v))    /* Wait for frame header */
  277.     {
  278.     default:
  279. #ifdef DEBUGLOG
  280.       xprsprintf (v->Msgbuf, "rzfile: zgethdr returned %ld\n", (long) c);
  281.       dlog (v, v->Msgbuf);
  282.       D (DEBUGINFO);
  283.  
  284. #endif
  285.       return ERROR;
  286.     case ZNAK:
  287.     case TIMEOUT:
  288.       if (--n < 0)
  289.         return ERROR;
  290. #ifdef DEBUGLOG
  291.       dlog (v, "rzfile: zgethdr NAK/Timeout\n");
  292.       D (DEBUGINFO);
  293. #endif
  294.       v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
  295.       xprsprintf (strchr (v->Msgbuf, '\0'), "@ %ld; %ld %s",
  296.               v->Rxbytes, (long) n, GetLocalString( &li, MSG_RETRIES_LEFT ));
  297.       v->xpru.xpru_errormsg = (char *) v->Msgbuf;
  298.       ++v->xpru.xpru_timeouts;
  299.       (*v->io.xpr_update) (&v->xpru);
  300.       continue;
  301.     case ZFILE:        /* Sender didn't see our ZRPOS yet; try again */
  302.       zrdata (v, v->Pktbuf, KSIZE);        /* Read and discard redundant */
  303.       continue;        /* filename packet */
  304.     case ZEOF:        /* End of file data */
  305.       if (v->Rxpos != v->Rxbytes)    /* We aren't in sync; go back */
  306.         {
  307.           xprsprintf (v->Msgbuf, GetLocalString( &li, MSG_BAD_EOF ),
  308.               v->Rxbytes, v->Rxpos);
  309.           upderr (v, v->Msgbuf);
  310.           continue;
  311.         }
  312.       bfclose (v);        /* All done; close file */
  313. #ifdef DEBUGLOG
  314.       dlog (v, "rzfile: EOF\n");
  315.       D (DEBUGINFO);
  316. #endif
  317.       updmsg (v, GetLocalString( &li, MSG_EOF_RECEIVED ));
  318.       return c;
  319.     case ERROR:        /* Too much garbage while waiting for frame header */
  320.       if (--n < 0)
  321.         return ERROR;
  322. #ifdef DEBUGLOG
  323.       dlog (v, "rzfile: zgethdr garbage overflow\n");
  324.       D (DEBUGINFO);
  325. #endif
  326.       v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
  327.       xprsprintf (strchr (v->Msgbuf, '\0'), "@ %ld; %ld %s",
  328.               v->Rxbytes, (long) n, GetLocalString( &li, MSG_RETRIES_LEFT ));
  329.       v->xpru.xpru_errormsg = (char *) v->Msgbuf;
  330.       ++v->xpru.xpru_errors;
  331.       (*v->io.xpr_update) (&v->xpru);
  332.       zmputs (v, v->Attn);
  333.       continue;
  334.     case ZDATA:        /* More file data packets forthcoming */
  335.       if (v->Rxpos != v->Rxbytes)    /* We aren't in sync; go back */
  336.         {
  337.           if (--n < 0)
  338.         return ERROR;
  339.           v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
  340.           xprsprintf (v->Msgbuf, GetLocalString( &li, MSG_DATA_AT_BAD_POSITION ),
  341.               v->Rxbytes, v->Rxpos);
  342.           v->xpru.xpru_errormsg = (char *) v->Msgbuf;
  343.           ++v->xpru.xpru_errors;
  344.           (*v->io.xpr_update) (&v->xpru);
  345.           zmputs (v, v->Attn);
  346.           continue;
  347.         }
  348.       /* Receive file data packet(s) */
  349.     moredata:
  350.       /* Give comm program its timeslice if it needs one */
  351.       if (v->io.xpr_chkmisc)
  352.         (*v->io.xpr_chkmisc) ();
  353.       /* Check for abort from comm program */
  354.       if (v->io.xpr_chkabort && (*v->io.xpr_chkabort) ())
  355.         goto aborted;
  356.       switch (c = zrdata (v, v->Pktbuf, KSIZE))
  357.         {
  358.         case ZCAN:
  359.         case RCDO:
  360.         aborted:
  361. #ifdef DEBUGLOG
  362.           dlog (v, "rzfile: zrdata returned CAN\n");
  363.           D (DEBUGINFO);
  364. #endif
  365.           upderr (v, GetLocalString( &li, MSG_TRANSFER_CANCELLED ));
  366.           return ERROR;
  367.         case ERROR:    /* CRC error or packet too long */
  368.           if (--n < 0)
  369.         return ERROR;
  370. #ifdef DEBUGLOG
  371.           dlog (v, "rzfile: zrdata returned error\n");
  372.           D (DEBUGINFO);
  373. #endif
  374.           v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
  375.           xprsprintf (strchr (v->Msgbuf, '\0'), "@ %ld; %ld %s",
  376.               v->Rxbytes, (long) n, GetLocalString( &li, MSG_RETRIES_LEFT ));
  377.           v->xpru.xpru_errormsg = (char *) v->Msgbuf;
  378.           ++v->xpru.xpru_errors;
  379.           (*v->io.xpr_update) (&v->xpru);
  380. #ifdef DEBUGLOG
  381.           dlog (v, v->Msgbuf);
  382.           dlog (v, "\n");
  383.           D (DEBUGINFO);
  384. #endif
  385.           zmputs (v, v->Attn);
  386.           continue;
  387.         case TIMEOUT:
  388.           if (--n < 0)
  389.         return ERROR;
  390. #ifdef DEBUGLOG
  391.           dlog (v, "rzfile: zrdata returned timeout\n");
  392.           D (DEBUGINFO);
  393. #endif
  394.           v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
  395.           xprsprintf (strchr (v->Msgbuf, '\0'), "@ %ld; %ld %s",
  396.               v->Rxbytes, (long) n, GetLocalString( &li, MSG_RETRIES_LEFT ));
  397.           v->xpru.xpru_errormsg = (char *) v->Msgbuf;
  398.           ++v->xpru.xpru_timeouts;
  399.           (*v->io.xpr_update) (&v->xpru);
  400. #ifdef DEBUGLOG
  401.           dlog (v, v->Msgbuf);
  402.           dlog (v, "\n");
  403.           D (DEBUGINFO);
  404. #endif
  405.           continue;
  406.         case GOTCRCW:    /* Sender says it's waiting for an ACK */
  407.           n = v->ErrorLimit;
  408.           if (putsec (v) == ERROR)
  409.         return ERROR;
  410.           stohdr (v, v->Rxbytes);
  411.           zshhdr (v, ZACK);
  412.           sendbuf (v);
  413.           goto nxthdr;
  414.         case GOTCRCQ:    /* Sender says it's not waiting, */
  415.           /* but ACK anyway (rarely used) */
  416.           n = v->ErrorLimit;
  417.           if (putsec (v) == ERROR)
  418.         return ERROR;
  419.           stohdr (v, v->Rxbytes);
  420.           zshhdr (v, ZACK);
  421.           sendbuf (v);
  422.           goto moredata;
  423.         case GOTCRCG:    /* Sender says keep receiving, there's more coming */
  424.           n = v->ErrorLimit;
  425.           if (putsec (v) == ERROR)
  426.         return ERROR;
  427.           goto moredata;
  428.         case GOTCRCE:    /* Sender says this is the last packet */
  429.           n = v->ErrorLimit;
  430.           if (putsec (v) == ERROR)
  431.         return ERROR;
  432.           goto nxthdr;
  433.         }
  434.     }
  435.     }
  436. }                /* End of short rzfile() */
  437.  
  438. /**********************************************************
  439.  *      short procheader(struct Vars *v)
  440.  *
  441.  * Process file name & info packet; either open file and
  442.  * prepare to receive, or return ERROR if we should skip
  443.  * this one for some reason
  444.  **********************************************************/
  445. short
  446. procheader (struct Vars *v)
  447. {
  448.   struct SetupVars *sv;
  449.   UBYTE *p, *openmode, buff[PATHLEN];
  450.   long n;
  451.  
  452. #ifdef DEBUGLOG
  453.   D (DEBUGINFO);
  454. #endif
  455.  
  456.   openmode = "w";
  457.   v->Strtpos = 0;
  458.  
  459.   /* Extract expected filesize from file info packet, if given */
  460.   v->Fsize = -1;
  461.   p = strchr (v->Pktbuf, '\0') + 1;
  462.   if (*p)
  463.     v->Fsize = atol (p);
  464.   /*
  465.      * Make sure we have room for file; skip it if not.
  466.      * Commented out for now, since getfree() isn't implemented yet.
  467.      if (v->Fsize > getfree())
  468.      {
  469.      xprsprintf(v->Msgbuf, GetLocalString( &li, MSG_INSUFFICIENT_DISK_SPACE ),
  470.      v->Fsize, getfree());
  471.      upderr(v, v->Msgbuf);
  472.      v->Noroom = TRUE;
  473.      return ERROR;
  474.      }
  475.    */
  476.  
  477.   /* If option RY set, use full received file path */
  478.   sv = (void *) v->io.xpr_data;
  479.   if (*sv->option_r == 'Y')
  480.     strcpy (v->Filename, v->Pktbuf);
  481.   else
  482.     {
  483.       /* else use the default directory path specified in the setup options */
  484.       strcpy (v->Filename, sv->option_p);
  485.       p = v->Filename + strlen (v->Filename) - 1;
  486.       if (p >= v->Filename && *p != '/' && *p != ':')
  487.     *++p = '/';
  488.       *++p = '\0';
  489.       /*
  490.          * Append the filename from the file info packet; ignore anything before
  491.          * last /, \, or : in filename (received directory path is ignored)
  492.        */
  493.       p = strchr (v->Pktbuf, '\0');    /* start at end and scan back */
  494.       /* to start of name */
  495.       while (p >= v->Pktbuf && *p != '/' && *p != '\\' && *p != ':')
  496.     --p;
  497.       strcat (v->Filename, ++p);
  498.     }
  499.  
  500.   /* Display name of file being received for user */
  501.   v->xpru.xpru_updatemask = XPRU_FILENAME;
  502.   v->xpru.xpru_filename = (char *) v->Filename;
  503.   (*v->io.xpr_update) (&v->xpru);
  504.  
  505.   /*
  506.      * If a file with this name already exists, handle in
  507.      * accordance with O option
  508.    */
  509.   if (exist (v))
  510.     {
  511.       switch (*sv->option_o)
  512.     {
  513.     case 'N':        /* Don't overwrite; change name to prevent collision */
  514.       strcpy (buff, v->Filename);
  515.       strcat (v->Filename, ".dup");
  516.       n = 2;
  517.       while (exist (v))
  518.         {
  519.           xprsprintf (v->Filename, "%s.dup%ld", buff, n);
  520.           ++n;
  521.         }
  522.       /* Update filename display to show new name */
  523.       (*v->io.xpr_update) (&v->xpru);
  524.       break;
  525.     case 'R':        /* Resume transfer from current end of file */
  526.       openmode = "a";
  527.       v->Strtpos = (*v->io.xpr_finfo) (v->Filename, 1L);
  528.       break;
  529.     case 'S':        /* Skip it */
  530.       upderr (v, GetLocalString( &li, MSG_FILE_EXISTS ));
  531.       return ERROR;
  532.       /* Else 'Y', go ahead and overwrite it (openmode = w) */
  533.     }
  534.     }
  535.  
  536.   /* Set text/binary mode according to options before opening file */
  537.   set_textmode (v);
  538.  
  539.   /*
  540.      * Figure out file translation mode to use; either binary (verbatim
  541.      * transfer) or ASCII (perform end-of-line conversions).  If user has
  542.      * specified a mode (TY or TN), that's what we use.  If user says use
  543.      * sender's suggestion (T?), set mode according to Zconv flag.  If neither
  544.      * side specifies, default to binary mode.
  545.    */
  546.   v->Thisbinary = v->Rxbinary || !v->Rxascii;
  547.   if (!v->Rxbinary && v->Zconv == ZCNL)
  548.     v->Thisbinary = FALSE;
  549.   if (!v->Rxascii && v->Zconv == ZCBIN)
  550.     v->Thisbinary = TRUE;
  551.  
  552.   /* Open the file (finally) */
  553.   if (!(v->File = bfopen (v, openmode)))
  554.     {
  555.       ++v->Errcnt;
  556.       upderr (v, GetLocalString( &li, MSG_CANT_OPEN_FILE ));
  557.       return ERROR;
  558.     }
  559.   getsystime (&v->Starttime);
  560.  
  561.   /* Initialize comm program transfer status display */
  562.   v->xpru.xpru_updatemask = XPRU_PROTOCOL | XPRU_FILESIZE | XPRU_MSG
  563.     | XPRU_BLOCKS | XPRU_ERRORS | XPRU_TIMEOUTS | XPRU_BLOCKCHECK
  564.     | XPRU_BYTES | XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE | XPRU_BLOCKSIZE;
  565.   v->xpru.xpru_protocol = "ZModem";
  566.   v->xpru.xpru_filesize = v->Fsize;
  567.   v->xpru.xpru_msg = (v->Thisbinary) ? GetLocalString( &li, MSG_RECEIVE_BINARY )
  568.     : GetLocalString( &li, MSG_RECEIVE_TEXT );
  569.   v->xpru.xpru_blocks = v->xpru.xpru_errors = v->xpru.xpru_timeouts = 0;
  570.   v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
  571.   v->xpru.xpru_bytes = v->Strtpos;
  572.   v->xpru.xpru_blocksize = v->ksize;
  573.   update_rate (v);
  574.   (*v->io.xpr_update) (&v->xpru);
  575.  
  576. #ifdef DEBUGLOG
  577.   D (DEBUGINFO);
  578. #endif
  579.  
  580.   return OK;
  581. }                /* End of short procheader() */
  582.  
  583. /**********************************************************
  584.  *      short putsec(struct Vars *v)
  585.  *
  586.  * Writes the received file data to the output file.
  587.  * If in ASCII mode, stops writing at first ^Z, and converts all
  588.  * \r\n pairs or solo \r's to \n's.
  589.  **********************************************************/
  590. short
  591. putsec (struct Vars *v)
  592. {
  593.   static char nl = '\n';
  594.   UBYTE *p;
  595.   short n;
  596.  
  597. #ifdef DEBUGLOG
  598.   D (DEBUGINFO);
  599. #endif
  600.  
  601.   /* If in binary mode, write it out verbatim */
  602.   if (v->Thisbinary)
  603.     {
  604.       if (bfwrite (v, v->Pktbuf, (long) v->Rxcount) != v->Rxcount)
  605.     goto diskfull;
  606.       /* If in text mode, perform end-of-line cleanup */
  607.     }
  608.   else
  609.     {
  610.       if (v->Eofseen)
  611.     return OK;
  612.       for (p = v->Pktbuf, n = v->Rxcount; --n >= 0; ++p)
  613.     {
  614.       if (*p == CPMEOF)
  615.         {
  616.           v->Eofseen = TRUE;
  617.           return OK;
  618.         }
  619.       else if (*p != '\n' && v->Lastsent == '\r')
  620.         {
  621.           if (bfwrite (v, &nl, 1L) != 1)
  622.         goto diskfull;
  623.         }
  624.       if (*p != '\r' && bfwrite (v, p, 1L) != 1)
  625.         goto diskfull;
  626.       v->Lastsent = *p;
  627.     }
  628.     }
  629.  
  630.   /* Update comm program status display */
  631.   v->xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_BLOCKSIZE | XPRU_BYTES
  632.     | XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE | XPRU_BLOCKCHECK;
  633.   ++v->xpru.xpru_blocks;
  634.   v->xpru.xpru_blocksize = v->Rxcount;
  635.   v->xpru.xpru_bytes = v->Rxbytes += v->Rxcount;
  636.   v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
  637.   update_rate (v);
  638.   (*v->io.xpr_update) (&v->xpru);
  639.  
  640.   return OK;
  641.  
  642. diskfull:
  643.   upderr (v, GetLocalString( &li, MSG_ERROR_WRITING_FILE ));
  644.   v->Noroom = TRUE;
  645.   return ERROR;
  646. }                /* End of short putsec() */
  647.  
  648. /**********************************************************
  649.  *      void ackbibi(struct Vars *v)
  650.  *
  651.  * End of batch transmission; disengage cleanly from sender
  652.  **********************************************************/
  653. void
  654. ackbibi (struct Vars *v)
  655. {
  656.   short n;
  657.  
  658. #ifdef DEBUGLOG
  659.   dlog (v, "ackbibi:\n");
  660.   D (DEBUGINFO);
  661. #endif
  662.   stohdr (v, 0L);
  663.   for (n = 4; --n;)
  664.     {
  665.       zshhdr (v, ZFIN);
  666.       sendbuf (v);
  667.       switch (readock (v, 100))
  668.     {
  669.     case 'O':
  670.       readock (v, 1);    /* Discard 2nd 'O' */
  671.     case TIMEOUT:
  672.     case RCDO:
  673.       return;
  674.     }
  675.     }
  676. }                /* End of void ackbibi() */
  677. /* End of Receive.c source */
  678.